/*
 * Copyright 2010 Alessandro Bellucci
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.soupframework.gwt.designerpanel.interaction.keyboard.client;

import java.util.List;

import org.soupframework.gwt.designerpanel.client.designer.ContainerDesigner;
import org.soupframework.gwt.designerpanel.client.designer.Designer;
import org.soupframework.gwt.designerpanel.client.designer.OperatingMode;
import org.soupframework.gwt.designerpanel.client.interaction.InteractionHandler;
import org.soupframework.gwt.designerpanel.client.interaction.InteractionHandlerManager;
import org.soupframework.gwt.designerpanel.client.interaction.impl.BaseContainerInteractionHandler;

import com.google.gwt.event.dom.client.HasKeyDownHandlers;
import com.google.gwt.event.dom.client.HasKeyPressHandlers;
import com.google.gwt.event.dom.client.HasKeyUpHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.shared.GwtEvent;

public abstract class ContainerKeyboardInteractionHandler<T, CD extends ContainerDesigner<T>> extends BaseContainerInteractionHandler<T, CD> implements KeyUpHandler, KeyDownHandler, KeyPressHandler {

	protected int keyDownEventCount = 0;

	public ContainerKeyboardInteractionHandler(InteractionHandlerManager<T> manager, CD designer){
		super(manager, designer);
	}

	@Override
	public void registerHandlersOnTarget(){
		if(!(this.getDesigner().getManager().getDesignerPanel() instanceof HasKeyDownHandlers)){
			throw new IllegalArgumentException("Target widget must implement HasKeyDownHandlers interface");
		}
		if(!(this.getDesigner().getManager().getDesignerPanel() instanceof HasKeyUpHandlers)){
			throw new IllegalArgumentException("Target widget must implement HasKeyUpHandlers interface");
		}
		if(!(this.getDesigner().getManager().getDesignerPanel() instanceof HasKeyPressHandlers)){
			throw new IllegalArgumentException("Target widget must implement HasKeyPressHandlers interface");
		}

		((HasKeyDownHandlers)this.getDesigner().getManager().getDesignerPanel()).addKeyDownHandler(this);
		((HasKeyUpHandlers)this.getDesigner().getManager().getDesignerPanel()).addKeyUpHandler(this);
		((HasKeyPressHandlers)this.getDesigner().getManager().getDesignerPanel()).addKeyPressHandler(this);
	}

	@Override
	public void onKeyDown(KeyDownEvent event) {
		this.dispatchEvent(event);
	}

	@Override
	public void onKeyUp(KeyUpEvent event) {
		this.dispatchEvent(event);
	}

	@Override
	public void onKeyPress(KeyPressEvent event) {
		this.dispatchEvent(event);
	}

	protected boolean dispatchEvent(GwtEvent<?> event) {
		if(!(this.getDesigner().getManager().getOperatingMode() == OperatingMode.DESIGN_MODE || this.getDesigner().getManager().getOperatingMode() == OperatingMode.LIVE_DESIGN_MODE)){
			// events are only handled in DESIGN or LIVE DESIGN modes
			return true;
		}

		Designer<T> childDesigner = this.dispatchEventToChildren(event);
		boolean childRet = childDesigner != null;
		boolean localRet = this.dispatchEventInt(event, childDesigner);
		return localRet || childRet;
	}

	protected boolean dispatchEventInt(GwtEvent<?> event, Designer<T> childDesigner) {
		if(event instanceof KeyDownEvent){
			this.keyDownEventCount ++;
			this.onKeyDownInt((KeyDownEvent)event, childDesigner);
			return true;
		}
		else if(event instanceof KeyUpEvent){
			this.onKeyUpInt((KeyUpEvent)event, childDesigner);
			return true;
		}
		else if(event instanceof KeyPressEvent){
			this.onKeyPressInt((KeyPressEvent)event, childDesigner);
			return true;
		}
		return false;
	}

	/**
	 * Dispatch an event to interested child designers
	 *
	 * @param event The event
	 * @return the {@link Designer} that received the event,null otherwise
	 */
	@SuppressWarnings("unchecked")
	protected Designer<T> dispatchEventToChildren(GwtEvent<?> event) {
		for(Designer<T> childDesigner : this.getDesigner().getChildrenDesigners()) {
			InteractionHandler<T> childInteractionHandler = this.getManager().getInteractionHandler(childDesigner);
			if(childInteractionHandler != null) {
				if(childInteractionHandler instanceof ContainerKeyboardInteractionHandler){
					ContainerKeyboardInteractionHandler<T, ?> mouseInteractionHandler = ((ContainerKeyboardInteractionHandler<T, ?>)childInteractionHandler);
					mouseInteractionHandler.dispatchEvent(event);
					return childDesigner;
				}
			}
		}
		return null;
	}

	/**
	 * Handles a {@link KeyDownEvent}
	 *
	 * @param event The {@link KeyDownEvent}
	 * @param childDesigner The child {@link Designer} that already handled this event
	 */
	protected void onKeyDownInt(KeyDownEvent event, Designer<T> childDesigner) {
		int keyCode = event.getNativeKeyCode();
		if(keyCode == KeyCodes.KEY_DELETE){
			List<T> selectedObjects = this.getDesigner().getSelectedObjects(false);
			this.getDesignerManager().beginCommandGroup("removeSelectedObjects");
			for(T object : selectedObjects){
				this.getDesigner().removeObject(object);
			}
			this.getDesignerManager().endCommandGroup("removeSelectedObjects");
		}
		else if(keyCode == KeyCodes.KEY_ESCAPE){
			this.getDesignerManager().getDesignerPanel().hideContextMenu();
		}
	}

	/**
	 * Handles a {@link KeyUpEvent}
	 *
	 * @param event The {@link KeyUpEvent}
	 * @param childDesigner The child {@link Designer} that already handled this event
	 */
	protected void onKeyUpInt(KeyUpEvent event, Designer<T> childDesigner) {
	}

	/**
	 * Handles a {@link KeyPressEvent}
	 *
	 * @param event The {@link KeyPressEvent}
	 * @param childDesigner The child {@link Designer} that already handled this event
	 */
	protected void onKeyPressInt(KeyPressEvent event, Designer<T> childDesigner) {
	}
}
